#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>

#define SEP " "
#define SIZE 128
#define NUM 1024

#define DROP_SPACE(s) do { while(isspace(*s)) s++;}while(0)

#define NONE_REDIR -1
#define INPUT_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2

int g_redir_flag = NONE_REDIR;
char *g_redir_filename = NULL;

char command_line[NUM];
char* command_args[SIZE];

char env_buffer[NUM];
extern char** environ;

int ChangeDir(const char* new_path)
{
  chdir(new_path);
  return 0;//调用成功
}

void PutEnvInMyShell(char* new_env)
{
  putenv(new_env);
}

void CheckDir(char* commands)
{
  assert(commands);
  //只关心有没有 大于或者 小于符号
  char* start = commands;
  char* end = commands+strlen(commands);//end指向字符串的结束
  while(start < end)
  {
    if(*start == '>')
    {
        if(*(start+1) == '>')
        {
          //追加
          *start = '\0';
          start+=2;
          g_redir_flag = APPEND_REDIR;
          DROP_SPACE(start);
          g_redir_filename = start;
          break;
        }
        else
        {
          //ls -a -l > log.txt输出重定向
          *start = '\0';
          start++;
          DROP_SPACE(start);
          g_redir_flag = OUTPUT_REDIR;
          g_redir_filename = start;
          break;

        }
    }
    else if(*start == '<')
    {
      //输入重定向
      *start = '\0';
      start++;
      DROP_SPACE(start);
      g_redir_flag = INPUT_REDIR;
      g_redir_filename = start;
      break;
    }
    else
    {
      start++;
    }
  }
}

int main()
{
  //一个shell 本质上就是一个死循环
  while(1)
  {
    g_redir_flag = NONE_REDIR;
    g_redir_filename = NULL;
    //不关心获取这些属性的接口
    //1.显示提示符
    printf("[张三@我的主机名 当前目录]# ");
    fflush(stdout);
    //2.获取用户输入
    memset(command_line,'\0',sizeof(command_line)*sizeof(char));
    fgets(command_line,NUM,stdin);//获取 输入 stdin
    command_line[strlen(command_line) - 1] = '\0';//清空\n
    //printf("%s\n",command_line);
    
    // "ls -a -l>log.txt" -> "ls -a -l\0log.txt"
    CheckDir(command_line);

    //3."ls -l -a -i" --> "ls","-l","-a","-i" 字符串切分
    command_args[0] = strtok(command_line, SEP);
    int index = 1;
    //给ls添加颜色
    if(strcmp(command_args[0]/*程序名*/,"ls") == 0)
      command_args[index++] = (char*)"--color=auto";
    //strtok 截取成功 返回字符串起始地址
    //截取失败 返回NULL
    while(command_args[index++] = strtok(NULL,SEP));
    // for debug
    //for(int i = 0;i<index;++i) 
    //{
    //  printf("%d:%s\n",i,command_args[i]);
    //}
    
    //4.TODO
    //如果直接exec*执行cd,最多只是让子进程进行路径切换，
    //子进程是一运行就完毕的进程！我们在shell中，更希望
    //父进程的路径切换
    //如果有些行为必须让父进程shell执行，不想让子进程
    //这种情况下不能创建子进程，只能让父进程自己实现对应的代码
    //这部分由父shell自己执行的命令称之为内建命令
    if(strcmp(command_args[0],"cd") == 0 && command_args[1] != NULL)
    {
      ChangeDir(command_args[1]);
      continue;
    }

    if(strcmp(command_args[0],"export") == 0 && command_args[1] != NULL)
    {
      strcpy(env_buffer,command_args[1]);
      PutEnvInMyShell(env_buffer);
      continue;
    }

    //5.创建子进程
    pid_t id = fork();
    if(id == 0)
    {
      int fd = -1;
      switch (g_redir_flag)
      {
        case NONE_REDIR:
          break;
        case INPUT_REDIR:
          fd = open(g_redir_filename,O_RDONLY);
          dup2(fd,0);
          break;
        case OUTPUT_REDIR:
          fd = open(g_redir_filename,O_WRONLY | O_CREAT | O_TRUNC);
          dup2(fd,1);
          break;
        case APPEND_REDIR:
          fd = open(g_redir_filename,O_WRONLY | O_CREAT | O_APPEND);
          dup2(fd,1);
          break;
        default:
          printf("BUG?\n");
          break;
      }
      //child
      //6.程序替换 会影响曾经子进程打开的文件吗？  不影响
      execvp(command_args[0],/*里面保存的就是执行的名字*/command_args);

      exit(1);//执行到这里一定失败了
    }
    int status = 0;
    pid_t ret = waitpid(id,&status,0);
    if(ret>0)
    {
      printf("等待子进程成功: sig:%d, code:%d\n",status&0x7F,(status>>8)&0xFF);
    }
  }

  return 0;
}
